/*
 * 
 * Copyright 2011 visual-analysis-of-cancer.
 * This file is part of visual-analysis-of-cancer.
 * 
 * visual-analysis-of-cancer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * visual-analysis-of-cancer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with kOSMik.  If not, see <http://www.gnu.org/licenses/>.
 *  
 * visual-analysis-of-cancer:
 * An tool to analye the WHO and GLOBOCAN cancer data.
 * http://code.google.com/p/visual-analysis-of-cancer/
 * 
 * 
 * 
 * Authors: Erik Sniegula, Johannes Dahlke
 * 
 * Module:  Programming in Processing for Visual Analytics
 * Group:   20
 * 
 * Created: Aug. 2011
 * Version: 1.0
 *
 */
 




package de.fuberlin.processing.who.cancer.gui;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Observable;
import java.util.Observer;


import processing.core.PApplet;
import processing.core.PFont;
import controlP5.ControlCanvas;
import controlP5.ControlEvent;
import controlP5.ControlGroup;
import controlP5.ControlListener;
import controlP5.ControlP5;
import controlP5.Controller;
import controlP5.ControllerGroup;
import controlP5.Tab;
import de.fuberlin.processing.who.cancer.data.DBException;
import de.fuberlin.processing.who.cancer.data.Database;
import de.fuberlin.processing.who.cancer.data.DatabaseConnector;
import de.fuberlin.processing.who.cancer.gui.healthgraph.CancerDisc;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell;
import de.fuberlin.processing.who.cancer.gui.scatterplot.CellConstructionAndProcessingParcel;
import de.fuberlin.processing.who.cancer.gui.scatterplot.CellDataProcessor;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_01_LifeExpectenyToHealthFinancing;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_02_HealthFinancingToCancerIncidence;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_03_LifeExpectancyToCancerIncidence;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_04_CancerIncidenceToCancerMortality;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_05_HealthFinancingToCancerMortality;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_06_LifeExpectancyToCancerMortality;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_07_CancerMortalityToAirPollution;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_08_CancerIncidenceToAirPollution;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_09_HealthFinancingToAirPollution;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_10_LifeExpectancyToAirPollution;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_11_CancerMortalityToUvRadiation;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_12_CancerIncidenceToUvRadiation;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_13_HealthFinancingToUvRadiation;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_14_LifeExpectancyToUvRadiation;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_15_CancerMortalityToAge;
import de.fuberlin.processing.who.cancer.gui.scatterplot.Cell_16_CancerIncidenceToAge;
import de.fuberlin.processing.who.cancer.gui.scatterplot.CustomCell;
import de.fuberlin.processing.who.cancer.interaction.CancerCauses;
import de.fuberlin.processing.who.cancer.interaction.CausesBroadcaster;
import de.fuberlin.processing.who.cancer.interaction.LocationBroadcaster;
import de.fuberlin.processing.who.cancer.interaction.LocationModifier;
import de.fuberlin.processing.who.cancer.interaction.TreatedVariables;
import de.fuberlin.processing.who.cancer.utils.Fonts;
import de.fuberlin.processing.who.cancer.utils.Notification;
import de.fuberlin.processing.who.cancer.utils.Test;



public class Scatterplot extends Controller implements Observer, ControlListener {

	public static final int NUM_TOP_TITLE_ROW_HEIGHT = 75;
	public static final int NUM_LEFT_TITLE_COL_WIDTH = 75;
	public static final int NUM_CELL_WIDTH = 75;
	public static final int NUM_CELL_HEIGHT = 75;
	public static final int NUM_PLOT_WIDTH = NUM_LEFT_TITLE_COL_WIDTH + 4 * NUM_CELL_WIDTH;
	public static final int NUM_PLOT_HEIGHT = NUM_TOP_TITLE_ROW_HEIGHT + 6 * NUM_CELL_HEIGHT;
	private LocationModifier locationModifier;
	private Cell[] cells;
	private DatabaseConnector db;
	private float cellWidth, cellHeight;
	private float plotX1, plotY1, plotX2, plotY2;
	//private float x, y, w, h;
	//private float margin;
	private PFont font;
	private ControlP5 controlP5;
	private ControlGroup controlGroup;
	private int canvasColor;
	private Cell selectedCell = null; 
	private CellDataProcessor cellDataProcessor = null;
	private final CellConstructionAndProcessingParcel[] cellConstructionAndProcessingParcels = {
			new CellConstructionAndProcessingParcel( "Cell_01_LifeExpectenyToHealthFinancing", "getLifeExpectancyByHealthFinancing", TreatedVariables.LIFE_EXPECTANCY, TreatedVariables.HEALTH_FINANCING),
			new CellConstructionAndProcessingParcel( "Cell_02_HealthFinancingToCancerIncidence", "getHealthFinancingToCancerIncidence", TreatedVariables.HEALTH_FINANCING, TreatedVariables.CANCER_INCIDENCE),
			new CellConstructionAndProcessingParcel( "Cell_03_LifeExpectancyToCancerIncidence", "getLifeExpectancyToCancerIncidence", TreatedVariables.LIFE_EXPECTANCY, TreatedVariables.CANCER_INCIDENCE),
			new CellConstructionAndProcessingParcel( "Cell_04_CancerIncidenceToCancerMortality", "getCancerIncidenceToCancerMortality", TreatedVariables.CANCER_INCIDENCE, TreatedVariables.CANCER_MORTALITY),
			new CellConstructionAndProcessingParcel( "Cell_05_HealthFinancingToCancerMortality", "getCancerMortalityToHealthFinancing", TreatedVariables.HEALTH_FINANCING, TreatedVariables.CANCER_MORTALITY),
			new CellConstructionAndProcessingParcel( "Cell_06_LifeExpectancyToCancerMortality", "getCancerMortalityToLifeExpectancy", TreatedVariables.LIFE_EXPECTANCY, TreatedVariables.CANCER_MORTALITY),
			new CellConstructionAndProcessingParcel( "Cell_07_CancerMortalityToAirPollution", "getCancerMortalityToAirPollution", TreatedVariables.CANCER_MORTALITY, TreatedVariables.CANCER_CAUSE_AIR_POLLUTION),
			new CellConstructionAndProcessingParcel( "Cell_08_CancerIncidenceToAirPollution", "getCancerIncidenceToAirPollution", TreatedVariables.CANCER_INCIDENCE, TreatedVariables.CANCER_CAUSE_AIR_POLLUTION),
			new CellConstructionAndProcessingParcel( "Cell_09_HealthFinancingToAirPollution", "getHealthFinancingToAirPollution", TreatedVariables.HEALTH_FINANCING, TreatedVariables.CANCER_CAUSE_AIR_POLLUTION),
			new CellConstructionAndProcessingParcel( "Cell_10_LifeExpectancyToAirPollution", "getLifeExpectancyToAirPollution", TreatedVariables.LIFE_EXPECTANCY, TreatedVariables.CANCER_CAUSE_AIR_POLLUTION),
			new CellConstructionAndProcessingParcel( "Cell_11_CancerMortalityToUvRadiation", "getCancerMortalityToUvRadiation", TreatedVariables.CANCER_MORTALITY, TreatedVariables.CANCER_CAUSE_UV_RADIATION),
			new CellConstructionAndProcessingParcel( "Cell_12_CancerIncidenceToUvRadiation", "getCancerIncidenceToUvRadiation", TreatedVariables.CANCER_INCIDENCE, TreatedVariables.CANCER_CAUSE_UV_RADIATION),
			new CellConstructionAndProcessingParcel( "Cell_13_HealthFinancingToUvRadiation", "getHealthFinancingToUvRadiation", TreatedVariables.HEALTH_FINANCING, TreatedVariables.CANCER_CAUSE_UV_RADIATION),
			new CellConstructionAndProcessingParcel( "Cell_14_LifeExpectancyToUvRadiation", "getLifeExpectancyToUvRadiation", TreatedVariables.LIFE_EXPECTANCY, TreatedVariables.CANCER_CAUSE_UV_RADIATION),
			new CellConstructionAndProcessingParcel( "Cell_15_CancerMortalityToAge", "getCancerMortalityToAge", TreatedVariables.CANCER_MORTALITY, TreatedVariables.CANCER_CAUSE_AGE),
			new CellConstructionAndProcessingParcel( "Cell_16_CancerIncidenceToAge", "getCancerIncidenceToAge", TreatedVariables.CANCER_INCIDENCE, TreatedVariables.CANCER_CAUSE_AGE)
	};
	
	


	public Scatterplot( 
			ControlP5 theControlP5, String theName, int x, int y) {
		super( theControlP5, (Tab) ( theControlP5.getTab( "default")), theName, x,
				y, NUM_PLOT_WIDTH, NUM_PLOT_HEIGHT);
		
	  // we want to be informed about changes of location
		this.locationModifier = LocationBroadcaster.get();
		this.locationModifier.addObserver( this);
  	
		// We want to know the cause, which we have to visualize
		CausesBroadcaster.get().addObserver( this);

		this.getCaptionLabel().setVisible( false);
		this.controlP5 = theControlP5;

		connectToDatabase();
		inits();
		initControlGroup();
		initCells();
	
	}


	/** initialize all necessary variables */
	private void inits() {
		this.font = Fonts.getDejavuSans();
	}
	
	private void initControlGroup() {
		controlGroup = controlP5.addGroup( "scatterplotControlGroup", (int) this.position.x, (int) this.position.y,
				this.getWidth());
		controlGroup.hideBar();
		controlGroup.setHeight( this.getHeight());
		this.canvasColor = controlP5.papplet.color( 0, 54, 82);

		controlGroup.addCanvas( new ControlCanvas() {

			@Override
			public void draw( PApplet p) {
				final String cellTitles[] = { 
						"life-\n expectancy",
						"health-\n financing",
						"cancer-\n incidence",
						"cancer-\n mortality",
						"air-\n pollution",
						"uv-\n radiation",
						"age"
				}; 
				
				p.pushStyle();
				
				// draw Frame
				p.stroke( canvasColor);
				p.strokeWeight( 2);				
				p.line( 0, height, width, height);
				p.line( width, 0, width, height);
				p.line( width -NUM_CELL_WIDTH, 0, width, 0);
				p.line( 0, height -3*NUM_CELL_HEIGHT, 0, height);
				
				
				// draw labels
				p.fill( 0);
				p.stroke( 0);
				p.textFont( font, 11);
				p.textAlign( CENTER);
				
				int i = 0;
				for ( int row = 0; row < 7; row++) {
					int col = Math.max( Math.min( 4, 4 - row), 0);
					p.pushMatrix();
					p.translate( col * NUM_CELL_WIDTH + ( NUM_CELL_WIDTH / 2), row * NUM_CELL_HEIGHT + ( NUM_CELL_HEIGHT / 2));
					p.text( cellTitles[i++], 0, 0);
					p.popMatrix();
				}
				
				p.popStyle();
			}
		});
		
	}

	/** connect with the database */
	private void connectToDatabase() {
		try {
			db = Database.get();
		} catch ( DBException e) {
			e.printStackTrace();
		}
	}



	/**
	 * Create all Cells for the ScatterPlot
	 * 
	 */
	private void initCells() {
		
		
		final String cellClassPackageName = "de.fuberlin.processing.who.cancer.gui.scatterplot.";

		
		Class[] cellArgumentsClasses = new Class[] { 
				ControlP5.class,
				ControllerGroup.class,
				String.class,
				CellConstructionAndProcessingParcel.class,
				Collection.class,
        float.class,
				float.class, 
				int.class, 
				int.class
		};
		
		Object[] cellArguments;
		Class cellDefinition;
		Constructor cellConstructor;
	
		Class dbClass = db.getClass();
	
		
		
		cells = new CustomCell[16];
		int i = 0;
		outerLoop: for ( int row = 0; row < 6; row++) {
			int startCol = Math.max( Math.min( 3, 3 - row), 0);
			for ( int col = startCol; col < 4; col++) {

				try {
					cellDefinition = Class
							.forName( cellClassPackageName + cellConstructionAndProcessingParcels[i].constructorName);
					cellConstructor = cellDefinition
							.getConstructor( cellArgumentsClasses);

					Method dbMethod = dbClass.getMethod( cellConstructionAndProcessingParcels[i].dbAccessMethodName, null);
				  Collection data = (Collection) dbMethod.invoke( db, null);
					
					cellArguments = new Object[] { controlP5, controlGroup,
							"scatterplotCell" + i, cellConstructionAndProcessingParcels[i], data,
							NUM_LEFT_TITLE_COL_WIDTH + col * NUM_CELL_WIDTH,
							NUM_TOP_TITLE_ROW_HEIGHT + row * NUM_CELL_HEIGHT, NUM_CELL_WIDTH,
							NUM_CELL_HEIGHT};

					Cell newCell = (Cell) cellConstructor.newInstance( cellArguments);
					( (Controller) newCell).addListener( this);
					cells[i++] = newCell;
				} catch ( Exception e) {
					Notification.printDebugException( e);
				}
				if ( i >= cells.length) {
					break outerLoop;
				}
			}
		}

		
		// Zelle Life-Expectancy zu Health-Financing 
		// zeigt einen Scatterplot des Healthgraphs an
		// -> hervorheben
		// Entscheidung revidiert -> decomment
		// cells[0].highlight();
		
	}

	
	@Override
	public void draw( PApplet theApplet) {
		// nothing todo. But do not remove!!!. It's neccessary
	}

	@Override
	public Controller setValue( float theValue) {
		// TODO Auto-generated method stub
		return null;
	}

	
	
	public void setCellDataProcessor( CellDataProcessor cellDataProcessor) {
		this.cellDataProcessor = cellDataProcessor;
	}

	@Override
	public void update( Observable observable, Object arg) {
		if ( observable instanceof LocationBroadcaster) {
			locationModifier.getLocations();
			Collection<Integer> selectedLocations = locationModifier.getLocations();
			updateLocations( selectedLocations);
		} else if ( observable instanceof CausesBroadcaster) {
			CancerCauses cancerCause = (CancerCauses) arg;
			updateCancerCause( cancerCause);
		}
	}

	
	private void updateLocations( final Collection<Integer> selectedLocations) {
		// Zellen holen sich locationset selbsttätig.
		update();
	}
	
	private void highlightCells( Cell ... cells) {
		for ( Cell cell : cells) {
			cell.highlight();
		}
	}
	
  private void normalizeCells( Cell ... cells) {
		for ( Cell cell : cells) {
			cell.beNormal();
		}
	}
  
  private Cell[] getCellsOfRange( int startIndex, int endIndex) {
  	Cell[] result = new Cell[ endIndex -startIndex +1];
  	for ( int n = startIndex; n <= endIndex; n++) {
  		result[n-startIndex] = cells[n];
		}
  	return result;
  }
	

	private void updateCancerCause( CancerCauses cancerCause) {
		switch ( cancerCause) {
			case AIR_POLLUTION:
				highlightCells( getCellsOfRange( 6, 9));
				normalizeCells( getCellsOfRange( 10, 15));
				break;
			case UV_RADIATION:
				normalizeCells( getCellsOfRange( 6, 9));
				highlightCells( getCellsOfRange( 10, 13));
				normalizeCells( getCellsOfRange( 14, 15));
				break;
			case AGE:
				normalizeCells( getCellsOfRange( 6, 13));
				highlightCells( getCellsOfRange( 14, 15));
				break;
			case SEX:
				normalizeCells( getCellsOfRange( 6, 15));
				break;
		

			default:
				break;
		}
	}

	@Override
	public void controlEvent( ControlEvent theEvent) {
		if ( theEvent.getController() instanceof CustomCell && 
				 new Float( theEvent.getValue()).equals( Cell.EVENT_MOUSE_PRESSED)) {
			CustomCell theCell = (CustomCell) theEvent.getController();
			if ( Test.isAssigned( selectedCell))
				selectedCell.unselect();
			selectedCell = theCell;
			theCell.select();
			cellDataProcessor.processDataFromCell( theCell);
		}
	}

}
